import { spawn } from 'child_process'
import iconv from 'iconv-lite'
import type { SpawnOptions } from 'child_process'
import { Writable } from 'stream'


export default class ChildProcess {

    messageList: string[] = []
    error: string = ""
    process: ReturnType<typeof spawn> | null = null
    encode: string = "gbk"

    constructor(
        public cmd: string,
        public args: string[],
        public options: Partial<SpawnOptions> = {}
    ) { }

    async run(
        callback?: (message: string, stdin: Writable | null | undefined, num: number) => void,
        timeout?: number
    ): Promise<{ messageList: string[], error: string }> {

        let num: number = 0
        let timeoutController: NodeJS.Timeout | null = null
        this.messageList = []
        this.error = ""

        this.process = spawn(this.cmd, this.args, {
            windowsHide: false,
            ...this.options,
        })

        this.process?.stdout?.on('data', (data: Buffer) => {
            const msglist = iconv.decode(data, this.encode).split("\r\n").filter(item => item.trim() != "")
            this.messageList.push(...msglist)
            if (callback) msglist.forEach(msg => callback(msg, this.process?.stdin, num++))
        })

        this.process?.stderr?.on('data', (data: Buffer) => {
            this.error = iconv.decode(data, this.encode)
            if (callback) callback(this.error, this.process?.stdin, num++)
        })


        if (timeout !== undefined) {
            timeoutController = setTimeout(() => {
                if (this.process?.killed) return
                this.error = "timeout"
                this.kill()
            }, timeout)
        }

        return await new Promise((resolve) => {
            this.process?.on('close', () => {
                clearTimeout(timeoutController!)
                resolve({ messageList: this.messageList, error: this.error })
            })
        })

    }

    kill(signal: NodeJS.Signals | number = "SIGKILL") {
        this.process?.kill(signal)
    }

    setEncode(encode: string) {
        this.encode = encode
    }

}